import { isNaNOrNull } from '@/widgets'

export interface LngLat {
  lng: number
  lat: number
}

export function lngLatEqual(p0: LngLat | undefined | null, p1: LngLat | undefined | null): boolean {
  if (!p0 && p0 === p1) {
    return true
  }
  if (!p0 || !p1) {
    return false
  }
  return p0.lng === p1.lng && p0.lat === p1.lat
}

export function checkLngLat(p: LngLat | undefined): boolean {
  if (!p) {
    return false
  }
  if (isNaNOrNull(p.lng) || isNaNOrNull(p.lat)) {
    return false
  }
  return Math.abs(p.lng) <= 180 && Math.abs(p.lat) <= 90
}

export const lngLat2str = formatLngLat
export const str2LngLat = parseLngLat

export function lng2str(lng: number | undefined | null): string {
  if (lng == null) {
    return ''
  }
  return lng < 0 ? 'W' + formatLngLatNumber(-lng) : 'E' + formatLngLatNumber(lng)
}
export function lat2str(lat: number | undefined | null): string {
  if (lat == null) {
    return ''
  }
  return lat < 0 ? 'S' + formatLngLatNumber(-lat) : 'N' + formatLngLatNumber(lat)
}

export function formatLngLat(pt: LngLat | undefined | null): string {
  if (!pt) {
    return ''
  }
  if (pt.lng == null || pt.lat == null) {
    return ''
  }

  const sLng = pt.lng < 0 ? 'W' + formatLngLatNumber(-pt.lng) : 'E' + formatLngLatNumber(pt.lng)
  const sLat = pt.lat < 0 ? 'S' + formatLngLatNumber(-pt.lat) : 'N' + formatLngLatNumber(pt.lat)
  return sLng + ',' + sLat
}

function formatLngLatNumber(l: number, eps: number = 0.001): string {
  l = Math.round((l * 3600) / eps) * eps
  let s = Math.floor(l)
  const ss = Math.floor((l - s) * 1000)
  let m = Math.floor(s / 60)
  s = s % 60
  const d = Math.floor(m / 60)
  m = m % 60
  if (ss !== 0) {
    return `${d}°${m}'${s}.` + `${ss}"`.padStart(4, '0')
  }
  if (s !== 0) {
    return `${d}°${m}'${s}"`
  }
  if (m !== 0) {
    return `${d}°${m}'`
  }
  return `${d}°`
}

export function parseLngLat(s: string | undefined): LngLat | undefined | null {
  if (!s) {
    return undefined
  }
  s = s.trim()
  if (!s) {
    return undefined
  }
  return parseLngLat0(s) || parseLngLat1(s) || parseLngLat2(s)
}

const lngLatRegex0 =
  /([EW+-]?)([\d.]+°?)([\d.]+')?([\d.]+")?,[ \t]*([NS+-]?)([\d.]+°?)([\d.]+')?([\d.]+")?/
function parseLngLat0(s: string): LngLat | undefined | null {
  const r = lngLatRegex0.exec(s)
  if (!r || r[0].length !== s.length) {
    return null
  }
  let lng = 0
  let lat = 0
  if (r[2]) {
    const d = parseFloat(r[2].replace('°', ''))
    if (d >= 0 && d <= 180) {
      lng = d
    } else {
      return null
    }
  }
  if (r[3]) {
    const m = parseFloat(r[3].replace("'", ''))
    if (m >= 0 && m < 60) {
      lng += m / 60.0
    } else {
      return null
    }
  }
  if (r[4]) {
    const s = parseFloat(r[4].replace('"', ''))
    if (s >= 0 && s < 60) {
      lng += s / 3600.0
    } else {
      return null
    }
  }
  if (r[1] === '-' || r[1] === 'W') {
    lng = -lng
  }
  if (r[6]) {
    const d = parseFloat(r[6].replace('°', ''))
    if (d >= 0 && d <= 90) {
      lat = d
    } else {
      return null
    }
  }
  if (r[7]) {
    const m = parseFloat(r[7].replace("'", ''))
    if (m >= 0 && m < 60) {
      lat += m / 60.0
    } else {
      return null
    }
  }
  if (r[8]) {
    const s = parseFloat(r[8].replace('"', ''))
    if (s >= 0 && s < 60) {
      lat += s / 3600.0
    } else {
      return null
    }
  }
  if (r[5] === '-' || r[5] === 'S') {
    lat = -lat
  }
  if (lng > -180 && lng <= 180 && lat > -90 && lat <= 90) {
    return { lng, lat }
  }
  return null
}

const lngLatRegex1 =
  /([EW+-]?)([\d.]+d?)([\d.]+m)?([\d.]+s)?,[ \t]*([NS+-]?)([\d.]+d?)([\d.]+m)?([\d.]+s)?/
function parseLngLat1(s: string): LngLat | undefined | null {
  const r = lngLatRegex1.exec(s)
  if (!r || r[0].length !== s.length) {
    return null
  }
  let lng = 0
  let lat = 0
  if (r[2]) {
    const d = parseFloat(r[2].replace('d', ''))
    if (d >= 0 && d <= 180) {
      lng = d
    } else {
      return null
    }
  }
  if (r[3]) {
    const m = parseFloat(r[3].replace('m', ''))
    if (m >= 0 && m < 60) {
      lng += m / 60.0
    } else {
      return null
    }
  }
  if (r[4]) {
    const s = parseFloat(r[4].replace('s', ''))
    if (s >= 0 && s < 60) {
      lng += s / 3600.0
    } else {
      return null
    }
  }
  if (r[1] === '-' || r[1] === 'W') {
    lng = -lng
  }
  if (r[6]) {
    const d = parseFloat(r[6].replace('d', ''))
    if (d >= 0 && d <= 90) {
      lat = d
    } else {
      return null
    }
  }
  if (r[7]) {
    const m = parseFloat(r[7].replace('m', ''))
    if (m >= 0 && m < 60) {
      lat += m / 60.0
    } else {
      return null
    }
  }
  if (r[8]) {
    const s = parseFloat(r[8].replace('s', ''))
    if (s >= 0 && s < 60) {
      lat += s / 3600.0
    } else {
      return null
    }
  }
  if (r[5] === '-' || r[5] === 'S') {
    lat = -lat
  }
  if (lng > -180 && lng <= 180 && lat > -90 && lat <= 90) {
    return { lng, lat }
  }
  return null
}

const lngLatRegex2 = /([EW])([\d]{7}(\.[\d]+)?),[ \t]*([NS])([\d]{6}(\.[\d]+)?)/

function parseLngLat2(s: string): LngLat | undefined | null {
  const r = lngLatRegex2.exec(s)
  if (!r || r[0].length !== s.length) {
    return null
  }
  let lng = parseFloat(r[2])
  if (isNaN(lng) || lng > 180) {
    return null
  }
  if (r[1] === 'W') {
    lng = -lng
  }
  let lat = parseFloat(r[5])
  if (isNaN(lat) || lat > 90) {
    return null
  }
  if (r[4] === 'S') {
    lat = -lat
  }
  return { lng, lat }
}
